package com.intertid.mongodb
import java.util.HashMap
import java.util.concurrent.locks.ReentrantReadWriteLock
import com.intertid.mongodb.commons.PackageUtils
import com.intertid.scala.commons._
import com.intertid.mongodb.annotations.Entity
import scala.reflect.BeanProperty
import sun.rmi.rmic.iiop.ClassPathLoader
import org.apache.commons.lang.ClassUtils
import scala.util.control.Breaks._
import org.apache.commons.lang.StringUtils
import java.lang.reflect.Field
import com.intertid.mongodb.annotations.Transient

class Mapper {

  private var packageName: String = _;

  def setPackageName(name: String) = {
    packageName = name;

  }

  def init() = {
    Mapper.init(packageName);
  }
}

object Mapper {

  class ClassInfo(_entityClass: Class[_], _fields: List[Field], _collectionName: String) {
    var entityClass: Class[_] = _entityClass;
    var fields: List[Field] = _fields;
    var collectionName: String = _collectionName;
  }
  private val lock = new ReentrantReadWriteLock;

  //val mappings = new HashMap[String, Class[_]];

  val mappings = new HashMap[Class[_], String];

  val classinfos = new java.util.ArrayList[ClassInfo];
  //init("com.intertid.mongodb");

  def init(packageName: String) = {
    try {
      lock.writeLock().lock();
      mappings.clear();

      //var p = Package.getPackage(packageName);

      val classes = PackageUtils.getClasses(packageName);

      classes.foreach(c => {
        var entitydefine: Entity = null;

        var superclassName: String = null;

        if (c.isAnnotationPresent(classOf[Entity])) {
          entitydefine = c.getAnnotation(classOf[Entity]);
        } else {
          val superclasses = ClassUtils.getAllSuperclasses(c);
          breakable {
            superclasses.foreach(sc => {

              entitydefine = if (sc.asInstanceOf[Class[_]].isAnnotationPresent(classOf[Entity])) {
                superclassName = sc.getClass().getSimpleName();
                sc.asInstanceOf[Class[_]].getAnnotation(classOf[Entity]);
              } else {
                null;
              }

              if (entitydefine != null)
                break;
            })
          }
        }

        if (entitydefine != null) {
          val collectionName = if (StringUtils.isEmpty(entitydefine.name())) { if (superclassName == null) c.getSimpleName() else { superclassName } } else entitydefine.name;
          mappings.put(c, collectionName);

          var fs: List[Field] = Nil;
          var curclass = c;
          while (!curclass.equals(classOf[Object])) {
            fs = fs ::: curclass.getDeclaredFields().toList.filter(f => !f.isAnnotationPresent(classOf[Transient]));
            curclass = curclass.getSuperclass();

          }

          val clsInfo = new ClassInfo(c, fs, collectionName);
          classinfos.add(clsInfo);
        }

        superclassName = null;
      });

    } finally {
      lock.writeLock().unlock();
    }

    mappings.foreach((cls, name) => {
      println("create mapping:" + cls + "->" + name);
    })
  }

  def exists(cls: Class[_]): Boolean = {
    try {
      lock.readLock().lock();
      return mappings.keySet().contains(cls.getRealClass());

    } finally {
      lock.readLock().unlock();
    }
  }

  def getCollectionName(cls: Class[_]): String = {
    try {
      lock.readLock().lock();

      return mappings.get(cls);

    } finally {
      lock.readLock().unlock();
    }
  }
}